#include "partition.h"
using namespace std;

//reset touch of nodes in partition
void resetTouch(int topLevel, int bottomLevel){

	for(int i = topLevel; i <= bottomLevel; i++){
		for(unsigned int j = 0; j < ptrMatrix[i].size(); j++){
			ptrMatrix[i][j]->touch = false;
		}
	}
}

vector<Node*>* ptrTransform(Node* np, int topLevel){

	queue<Node*> nodeQ;
	Node* nptr;
	vector<Node*>::iterator it;

	assert(topLevel < np->level);
	vector<Node*>* subPtr = new vector<Node*> [np->level - topLevel + 1];

	resetTouch(0, np->level);
	nodeQ.push(np);
	while(nodeQ.size() != 0){
		nptr = nodeQ.front();
		nodeQ.pop();
		if(nptr->level >= topLevel){
			subPtr[nptr->level - topLevel].push_back(nptr);
			if(nptr->level != topLevel){
				for(it = nptr->upperNodes.begin(); it < nptr->upperNodes.end(); it++){
					if(!(*it)->touch){
						nodeQ.push((*it));
						(*it)->touch = true;
					}
				}
			}
		}else{
			subPtr[0].push_back(nptr);
		}
	}

	return subPtr;
}

thrust::host_vector<devNode>* faninTransform(Node* np, int topLevel){

	queue<Node*> nodeQ;
	Node* nptr;
	devNode* dp;
	vector<Node*>::iterator it;

	assert(topLevel < np->level);
	thrust::host_vector<devNode>* subMatrix = new thrust::host_vector<devNode> [np->level - topLevel + 1];

	resetTouch(0, np->level);
	//initialize the queue
	nodeQ.push(np);

	//process the queue
	while(nodeQ.size() != 0){
		nptr = nodeQ.front();
		nodeQ.pop();
		if(nptr->level >= topLevel){
			nptr->offset = subMatrix[nptr->level - topLevel].size();
			dp = new devNode(nptr->modIndex, nptr->index, nptr->type, nptr->gate, nptr->fanin);
			subMatrix[nptr->level - topLevel].push_back(*dp);

			//only the fanin of node whose level is higher than top level will be pushed into queue 
			if(nptr->level != topLevel){
				for(it = nptr->upperNodes.begin(); it < nptr->upperNodes.end(); it++){
					if(!(*it)->touch){
						nodeQ.push((*it));
						(*it)->touch = true;
					}
				}
			}
		}else{
			//handle the case when fanin node is above partition level
			nptr->offset = subMatrix[0].size();
			dp = new devNode(nptr->modIndex, nptr->index, nptr->type, nptr->gate, nptr->fanin);
			subMatrix[0].push_back(*dp);
		}
	}

	resetTouch(0, np->level);
	//re-process queue to update fanin of each node
	nodeQ.push(np);
	while(nodeQ.size() != 0){
		nptr = nodeQ.front();
		nodeQ.pop();
		if(nptr->level > topLevel){
			for(int i = 0; i < nptr->fanin; i++){
				if(nptr->upperNodes[i]->level >= topLevel){
					subMatrix[nptr->level - topLevel][nptr->offset].setLevel(i, nptr->upperNodes[i]->level - topLevel);
					subMatrix[nptr->level - topLevel][nptr->offset].setFanin(i, nptr->upperNodes[i]->offset);
				}else{
					subMatrix[nptr->level - topLevel][nptr->offset].setLevel(i, 0);
					subMatrix[nptr->level - topLevel][nptr->offset].setFanin(i, nptr->upperNodes[i]->offset);
				}
			}

			for(it = nptr->upperNodes.begin(); it < nptr->upperNodes.end(); it++){
				if(!(*it)->touch){
					nodeQ.push((*it));
					(*it)->touch = true;
				}
			}
		}
	}

	return subMatrix;
}

void printVertex(vertex* vp){

	ofstream vResult;
	vResult.open("partition.txt", ofstream::app);
	vResult<<"subcircuit at vertex of "<<vp->vptr->index<<endl;

	for(int i = 0; i <= vp->bottomLevel - vp->topLevel; i++){
		for(unsigned int j = 0; j < vp->subckt[i].size(); j++){
			vResult<<vp->subckt[i][j].getIndex()<<" ";
		}
		vResult<<endl;
	}
	vResult<<endl;
	vResult.close();
}

void printVertexPtr(vertex* vp){

	ofstream vResult;
	vResult.open("partition_ptr.txt", ofstream::app);
	vResult<<"subcircuit at vertex of "<<vp->vptr->index<<endl;

	for(int i = 0; i <= vp->bottomLevel - vp->topLevel; i++){
		for(unsigned int j = 0; j < vp->subptr[i].size(); j++){
			vResult<<vp->subptr[i][j]->index<<" ";
		}
		vResult<<endl;
	}
	vResult<<endl;
	vResult.close();
}

void printPartition(){

	map<int, vector<vertex> >::iterator mit;
	vertex* vp;
	ofstream pResult;
	pResult.open("partition.txt", ofstream::out);
	pResult<<endl;
	pResult.close();
	
	for(mit = nPartition.begin(); mit != nPartition.end(); mit++){
		pResult.open("partition.txt", ofstream::app);
		pResult<<"the subcircuit below is partition at level "<<(*mit).first<<endl;
		pResult.close();
		for(unsigned int i = 0; i < (*mit).second.size(); i++){
			vp = &((*mit).second[i]);
			printVertex(vp);
		}
	}
}

void printPartitionPtr(){

	map<int, vector<vertex> >::iterator mit;
	vertex* vp;
	ofstream pResult;
	pResult.open("partition_ptr.txt", ofstream::out);
	pResult<<endl;
	pResult.close();

	for(mit = nPartition.begin(); mit != nPartition.end(); mit++){
		pResult.open("partition_ptr.txt", ofstream::app);
		pResult<<"the subcircuit below is partition at level "<<(*mit).first<<endl;
		pResult.close();
		for(unsigned int i = 0; i < (*mit).second.size(); i++){
			vp = &((*mit).second[i]);
			printVertexPtr(vp);
		}
	}
}

void partitionBuild(int level){

	/*
	vertex v1399;
	Node* np = moduleAll[0]->nAll[1399];
	faninTransform(np, 22, v1399.subckt);
	*/
	vertex* vp;
	vector<vertex> temp;
	vector<vertex>::iterator it;
	Node* np;

	/*
	for(unsigned int i = 0; i < ptrMatrix[level].size(); i++){
		vp = new vertex();
		vp->subckt = faninTransform(ptrMatrix[level][i], 0);
		vp->subptr = ptrTransform(ptrMatrix[level][i], 0);
		vp->topLevel = 0;
		vp->bottomLevel = level;
		vp->vptr = ptrMatrix[level][i];
		temp.push_back(*vp);
	}
	nPartition.insert(pair<int, vector<vertex> >(level, temp));
	*/
	for(unsigned int i = 0; i < ptrMatrix[maxLevel].size(); i++){
		vp = new vertex();
		vp->subckt = faninTransform(ptrMatrix[maxLevel][i], level);
		vp->subptr = ptrTransform(ptrMatrix[maxLevel][i], level);
		vp->topLevel = level;
		vp->bottomLevel = maxLevel;
		vp->vptr = ptrMatrix[maxLevel][i];
		temp.push_back(*vp);
	}
	nPartition.insert(pair<int, vector<vertex> >(maxLevel, temp));

	temp.clear();
	resetTouch(0, level);
	for(it = nPartition[maxLevel].begin(); it < nPartition[maxLevel].end(); it++){
		for(int i = 0; i < (*it).subptr[0].size(); i++){
			np = (*it).subptr[0][i];
			if(!np->touch){
				vp = new vertex();
				vp->subckt = faninTransform(np, 0);
				vp->subptr = ptrTransform(np, 0);
				vp->topLevel = 0;
				vp->bottomLevel = np->level;
				vp->vptr = np;
				temp.push_back(*vp);
				np->touch = true;
			}
		}
	}
	nPartition.insert(pair<int, vector<vertex> >(level, temp));
}

